package wsi.subgraph.tree;

import java.util.ArrayList;
import java.util.List;

import org.rygh.core.model.common.MapList;
import org.rygh.core.model.common.StringSet;
import org.rygh.core.model.tree.Tree;
import org.rygh.semantic.model.ex.PredicatePath;
import org.rygh.semantic.model.rdf.Predicate;

import wsi.resolve.FactItem;

public class EntityTree extends Tree<EntityObject, EntityLeaf> {

	private final MapList<StringSet, EntityLeaf> nodeMapList = new MapList<>();

	public EntityTree() {
		super(new EntityLeaf(null, null));
	}

	public boolean canMapToAnyNode(EntityLeaf node) {
		List<EntityLeaf> nodes = getRoot().getAllNodes();
		boolean mapped = false;

		Predicate pred = node.getValue().getPredicate();

		for (EntityLeaf n : nodes) {
			if (pred != null) {
				if (n == getRoot())
					continue;

				if (pred.equals(n.getValue().getPredicate())) {
					mapped = true;
					break;
				}
			} else {
				if (n == getRoot()) {
					mapped = true;
					break;
				}
			}
		}

		return mapped;
	}

	private boolean containsNodesAtPredicatePathOf(EntityLeaf node,
			PredicatePath path) {
		// System.out.printf("check node %s for path %s\n", node.getValue()
		// .getValue(), path.getKey());

		if (path.isEmpty())
			return true;

		Predicate pred, fpred = path.getFirstPredicate();
		PredicatePath tail = path.getTailPath();
		EntityLeaf parent = node.getParent();

		if (parent != null) {

			if (node.getValue().getPredicate() != null) {
				pred = new Predicate(node.getValue().getPredicate());
				pred.setInverse(!pred.isInverse()); // backward to parent

				if (pred.equals(fpred)) {
					if (containsNodesAtPredicatePathOf(parent, tail))
						return true;
				}
			}
		}

		if (!node.isLeaf()) {
			for (EntityLeaf child : node.getChildren()) {
				pred = new Predicate(child.getValue().getPredicate());

				if (pred.equals(fpred)) {
					if (containsNodesAtPredicatePathOf(child, tail))
						return true;
				}
			}
		}

		return false;
	}

	public boolean containsNodesAtPredicatePathOf(FactItem item) {
		EntityLeaf inputNode = getConcreteRoot();
		List<EntityLeaf> centers = getNodesAt(item.getInputToCenterPath(),
				inputNode);

		PredicatePath valuePath = item.getCenterToValulePath();

		for (EntityLeaf center : centers)
			if (containsNodesAtPredicatePathOf(center, valuePath))
				return true;

		return false;
	}

	@Override
	public EntityLeaf getConcreteRoot() {
		return root.getFirstChild();
	}

	/**
	 * here we use only StringSet equivalence to get the matched nodes. we
	 * should later extend this function to check equivalence between dates,
	 * numbers, countries (CN, US) etc.
	 * 
	 * @param key
	 * @return
	 */
	public List<EntityLeaf> getNodes(String value, boolean exact) {
		if (exact) {
			// System.out.println("get nodes " + value);

			for (StringSet key : nodeMapList.keySet())
				if (value.equals(key.getValue()))
					return nodeMapList.get(key);

			return null;
		}

		StringSet key = new StringSet(value);

		if (key.isEmpty()) {
			// System.out.println("empty key entity tree " + value);
			return null;
		}

		return nodeMapList.get(key);

		/*
		 * 130924. not working for last.fm/getArtistByName.
		 * 
		 * if we return partial confidence for a value with partial containment,
		 * then it becomes difficult to distinguish perfect match at the
		 * position 1.
		 */

		// if (nodeMapList.containsKey(key))
		// return nodeMapList.get(key);
		// else {
		// int node_len, leaf_len;
		// leaf_len = key.size();
		//
		// if (leaf_len <= 1)
		// return null;
		//
		// List<EntityNode> nodes = new ArrayList<>();
		// List<EntityNode> leaves = getAllLeaves();
		// StringSet ss;
		// Entity entity;
		//
		// for (EntityNode leaf : leaves) {
		// entity = leaf.getValue().getValue();
		// assert entity.isLiteral() : entity.getValue() + ", "
		// + leaf.getParent().getValue().getValue();
		//
		// ss = new StringSet(entity.getValue());
		// node_len = ss.size();
		//
		// if (node_len <= 1)
		// continue;
		//
		// if (leaf_len > node_len * 2)
		// continue;
		//
		// /*
		// * in the xml, the leaf contains more info than the entity like
		// * Blue - Sun Flower v.s. Sun Flower
		// */
		//
		// if (value.contains(entity.getValue())) {
		// // example: Marilyn Monroe Vocal<>Marilyn Monro
		// nodes.add(leaf);
		// }
		// }
		//
		// // if (!nodes.isEmpty())
		// // System.out.println("getNodes " + value + ": " + nodes);
		//
		// return nodes;
		// }
	}

	private void getNodesAt(EntityLeaf node, PredicatePath path,
			List<EntityLeaf> nodes) {
		PredicatePath tail = path.getTailPath();

		if (tail == null) {
			nodes.add(node);
			return;
		}

		Predicate pred, fpred = path.getFirstPredicate();

		if (node != getConcreteRoot()) {
			pred = node.getValue().getPredicate();

			if (pred.equals(fpred))
				getNodesAt(node.getParent(), tail, nodes);
		}

		for (EntityLeaf child : node.getChildren()) {
			pred = child.getValue().getPredicate();

			if (pred.equals(fpred))
				getNodesAt(child, tail, nodes);
		}
	}

	public List<EntityLeaf> getNodesAt(PredicatePath path, EntityLeaf startNode) {
		List<EntityLeaf> nodes = new ArrayList<>();

		if (path == null)
			nodes.add(startNode);
		else
			getNodesAt(startNode, path, nodes);

		return nodes;
	}

	@Override
	public EntityBranch link(EntityLeaf start, EntityLeaf end) {
		return new EntityBranch(super.link(start, end));
	}

	/**
	 * refrain from putting non-literal nodes here.
	 * 
	 * @param key
	 * @param node
	 */
	public void putNode(String key, EntityLeaf node) {
		assert node.getValue().getValue().isLiteral();
		nodeMapList.put(new StringSet(key), node);
	}

}
